/**
 * Object containing data and communicating with server.
 * @constructor
 */
Model = function(){};

/**
 * data container
 * @private
 */
Model.subjects_ = {};

/**
 * Model.user_.settings
 * @private
 */
Model.settings_ = {
  "sorted_by": "name",
  "sorted_asc": true,
  "selected_subjects": {},
  "selected_tests": {},
  "selected_running": {}
};

/**
 * information about user
 * @private
 */
Model.user_ = {
  "name": "",
  "isTeacher" : false
}

/**
 * Display subjects in sorted form.
 * @public
 * 
 * @param {string} by what subjects are to be sorted
 * @param {boolean} ascending
 * @param {boolean} clear selected items
 */
Model.refreshSubjects = function(by, ascending, erase, callback){
  if(typeof erase !== "boolean"){
    erase = false;
  }
  if(typeof by !== "string"){
    by = "name";
  }
  if(typeof ascending !== "boolean"){
    ascending = true;
  }
  if(erase){
    Model.settings_.selected_subjects = {};
    Model.settings_.selected_tests = {};
    Model.settings_.selected_running = {};
  }
  Model.settings_.sorted_by = by;
  Model.settings_.sorted_asc = ascending;
  View.refreshAllSubjects(Model.user_.isTeacher, Model.getAllSubjects_(), function(){
    View.displayOrder(by, ascending);
    for(var subjectId in Model.settings_.selected_subjects){
      View.subjectShow(Model.user_.isTeacher, Model.getSubject(subjectId));
    }
    for(var testId in Model.settings_.selected_tests){
      View.testShow(Model.user_.isTeacher, Model.getTest(testId));
    }
    for(var runId in Model.settings_.selected_running){
      View.runningShow(Model.user_.isTeacher, Model.getRunning(runId));
    }
    log("Model: Subjects refreshed.");
    if(typeof callback === "function") callback.call();
  });
}

Model.refreshAllSubjects = function(callback){
    $.get("/content", function(data, status) {
      if (data.error) {
        return View.errorShow("test_options_activate_test_" + testId, error);
      }
      Model.subjects_ = data;
      Model.refreshSubjects(undefined, undefined, undefined, callback);
    });
}

/**
 * Display or hide subject.
 * @public
 * 
 * @param {string} subjectId
 */
Model.toggleSubject = function(subjectId, callback){
  if(typeof subjectId !== "string"){
    return error(arguments);
  }
  if(subjectId in Model.settings_.selected_subjects){
    delete Model.settings_.selected_subjects[subjectId];
    for(var testId in Model.subjects_[subjectId].tests){
      delete Model.settings_.selected_tests[testId];
      for(var runId in Model.subjects_[subjectId].tests[testId].running){
        delete Model.settings_.selected_running[runId];
      }
    }
    View.subjectHide(subjectId, callback);
  }else{
    Model.settings_.selected_subjects[subjectId] = true;
    View.subjectShow(Model.user_.isTeacher, Model.getSubject(subjectId), callback);
  }
}

/**
 * Display or hide test.
 * @public
 * 
 * @param {string} testId
 */
Model.toggleTest = function(testId, callback){
  if(typeof testId !== "string"){
    return error(arguments);
  }
  if(testId in Model.settings_.selected_tests){
    delete Model.settings_.selected_tests[testId];
    var subjectId = "-1";
    for(sId in Model.subjects_){
      for(tId in Model.subjects_[sId].tests){
        if(tId !== testId) continue;
        subjectId = sId;
        break;
      }
      if(subjectId !== "-1") break;
    }
    for(var runId in Model.subjects_[subjectId].tests[testId].running){
      delete Model.settings_.selected_running[runId];
    }
    View.testHide(testId, callback);
  }else{
    Model.settings_.selected_tests[testId] = true;
    View.testShow(Model.user_.isTeacher, Model.getTest(testId), callback);
  }
}

/**
 * Display or hide running test.
 * @public
 * 
 * @param {string} runningId
 */
Model.toggleRunning = function(runningId, callback){
  if(typeof runningId !== "string"){
    return error(arguments);
  }
  if(runningId in Model.settings_.selected_running){
    delete Model.settings_.selected_running[runningId];
    var subjectId = -1;
    var testId = -1;
    for(sId in Model.subjects_){
      for(tId in Model.subjects_[sId].tests){
          for(rId in Model.subjects_[sId].tests[tId].running){
          if(rId !== runningId) continue;
          subjectId = sId;
          testId = tId;
          break;
        }
        if(testId !== -1) break;
      }
      if(subjectId !== -1) break;
    }
    View.runningHide(runningId, callback);
  }else{
    Model.settings_.selected_running[runningId] = true;
    View.runningShow(Model.user_.isTeacher, Model.getRunning(runningId), callback);
  }
}

/**
 * Return all subjects sorted.
 * @private
 * 
 * @returns {array}
 */
Model.getAllSubjects_ = function(){
  var result = [];
  for(var subjectId in Model.subjects_){
    var unreadMessagesCount = 0;
    var messagesCount = 0;
    var subject = {
      "id": subjectId,
      "name": Model.subjects_[subjectId].name,
      "todo": 0,
      "done": 0,
      "countOfMessages": 0
    };
    for(var testId in Model.subjects_[subjectId].tests){
      var isDone = false;
      var test = Model.subjects_[subjectId].tests[testId];
      for(var runningId in test.running){
        var run = test.running[runningId];
        if(run.done) isDone = true;
        for(var messageId in run.messages){
          subject.countOfMessages++;
        }
      }
      if(test.active) isDone ? subject.done++ : subject.todo++;
    }
    result.push(subject);
  }
  sortArray(result, Model.settings_.sorted_by, Model.settings_.sorted_asc);
  return result;
};

/**
 * Return content of subject with given id.
 * @private
 * 
 * @param {string} subjectId
 * @returns {object}
 */
Model.getSubject = function(subjectId){
  if(typeof subjectId !== "string"){
    return error(arguments);
  }
  var subject = Model.subjects_[subjectId];
  var returnValue = {
    "id" : subjectId,
    "name" : subject.name,
    "tests" : []
  }
  var tests = subject.tests;
  for(var testId in tests){
    var test = tests[testId];
    var messagesCount = 0;
    var testsDone = 0;
    var allTests = 0;
    for(var runningId in test.running){
      var run = test.running[runningId];
      if(run.done) testsDone++;
      allTests++;
      for(var messageId in run.messages){
        messagesCount++;
      }
    }
    returnValue.tests.push({
      "id" : testId,
      "name" : test.name,
      "testsDone": testsDone,
      "allTests": allTests,
      "deadline" : test.deadline,
      "messagesCount" : messagesCount,
      "active": test.active
    });
  }
  if(typeof returnValue === "undefined"){
      error("Model: subject with subjectId [" + subjectId + "] is undefined");
  }
  return returnValue;
}

/**
 * Return content of test with given id.
 * @private
 * 
 * @param {string} testId
 * @returns {object}
 */
Model.getTest = function(testId){
  if(typeof testId !== "string"){
    return error(arguments);
  }
  for(var sId in Model.subjects_){
    for(var tId in Model.subjects_[sId].tests){
      if(tId !== testId) continue;
      var test = Model.subjects_[sId].tests[tId];
      var returnValue = {
        "id" : testId,
        "name" : test.name,
        "count": 0,
        "active": test.active,
        "required_files": test.required_files,
        "running" : []
      }
      for(var rId in test.running){
        returnValue.count++;
        var messageCount = 0;
        var run = test.running[rId];
        for(var mId in run.messages){
            messageCount++;
        }
        returnValue.running.push({
          "id" : rId,
          "name": run.name,
          "done": run.done,
          "student": run.student,
          "submitted": run.submitted,
          "messages": messageCount
        });
      }
      if(typeof returnValue === "undefined"){
        error("Model: test with testId [" + testId + "] is undefined");
      }
      return returnValue;
    }
  }
}

/**
 * Return content of running test with given id.
 * @public
 * 
 * @param {string} runningId
 * @returns {object}
 */
Model.getRunning = function(runningId){
  if(typeof runningId !== "string"){
    return error(arguments);
  }
  for(var sId in Model.subjects_){
    for(var tId in Model.subjects_[sId].tests){
      for(var rId in Model.subjects_[sId].tests[tId].running){
        if(rId !== runningId) continue;
        var run = Model.subjects_[sId].tests[tId].running[rId];
        var returnValue = {
          "id": rId,
          "name": run.name,
          "done": run.done,
          "submitted": run.submitted,
          "messages": []
        };
        for(var mId in Model.subjects_[sId].tests[tId].running[rId].messages){
          var message = Model.subjects_[sId].tests[tId].running[rId].messages[mId];
          returnValue.messages.push({
            "id": mId,
            "read": message.read,
            "from": message.from,
            "content": message.content,
            "time": message.time
          });
        }
        if(typeof returnValue === "undefined"){
          error("Model: running with runningId [" + runningId + "] is undefined");
        }
        return returnValue;
      }
    }
  }
}

/**
 * Validate login and password lengths
 * @public
 * 
 * @returns {boolean}
 */
Model.loginValidation = function(name, password, callback){
  if(name.length === 0){
    View.errorShow('button_login', "empty_login");
    return false;
  }
  if(name.length < 5){
    View.errorShow('button_login', "short_login");
    return false;
  }
  if(password.length === 0){
    View.errorShow('button_login', "empty_password");
    return false;
  }
  if(password.length < 5){
    View.errorShow('button_login', "short_password");
    return false;
  }
  return Model.validationFromServer_(name, password, callback);
}

/**
 * Validates login and password.
 * @private
 * 
 * @returns {boolean}
 */
Model.validationFromServer_ = function(name, password, callback) {
  View.spinnerShow('button_login');
  $.post("/login",
  {
    "name": name,
    "password": password
  },
  function(data, status) {
    if (data.error) {
      View.errorShow('button_login', data.error);
      View.spinnerHide('button_login');
      return;
    }
    $.get("/content", function(data, status) {
      if (data.error) {
        View.spinnerHide('button_login');
        return View.errorShow('button_login', data.error);
      }
      Model.user_.isTeacher = name[0] !== 'x';
      Model.user_.name = name;
      Model.subjects_ = data;
      Model.refreshSubjects(undefined, undefined, undefined, callback);
      View.spinnerHide('button_login');
    });
  });
}

/**
 * Removes all the subjects.
 * @private
 */
Model.clearSubjects_ = function(){
  subjects = {};
  students = {};
  Model.settings_.selected_subjects = {};
  Model.settings_.selected_tests = {};
  Model.settings_.selected_running = {};
  Model.refreshSubjects();
  Model.user_.name = "";
  Model.user_.isTeacher = false;
  log("Model: Subjects cleared");
}

/**
 * Send subjects to server
 * @public
 */
Model.sendSubjectsToServer = function(){
  $.post("/logout", {},
  function(data, status){
    Model.clearSubjects_();
    View.transitionLogout();
    log("Model: Subjects sent to server");
  });
}

/**
 * Adds test.
 * @public
 * 
 * @param {object{name, done, deadline, running}}
 * @param {string}
 */
Model.addTest = function(test, subjectId, callback){
  if(typeof test !== "object" ||
     typeof test.name !== "string"){
    return error(arguments);
  }
  $.post("/add_test",
  {
    "subjectId": subjectId,
    "name": test.name,
    "compileCommand": test.compilationCommand,
    "runCommand": test.runningCommand,
    "solutionFilename": test.solutionFilename,
    "deadline": test.deadline
  },
  function(data, status){
    if(typeof data.error === "string"){
        View.errorShow("test_options_activate_test_" + test.id, data.error);
        return;
    }
    Model.refreshAllSubjects(callback);
    log("Model: Test added");
  });
}

/**
 * Adds new running test.
 * @public
 * 
 * @param {object{name, done, submitted, file, messages}}
 * @param {string}
 */
Model.addRunning = function(buttonId, error, callback){
  Model.refreshAllSubjects(function() {
    if (typeof error === "string") {
      View.errorShow(buttonId, error);
    }
    callback();
  });
}

Model.activateTest = function(testId, callback){
  if(typeof testId !== "string"){
    return error(arguments);
  }
  $.post("/activate_test",
         {"testId": testId},
         function(data, status) {
         if(typeof data.error === "string"){
           View.errorShow("test_options_submit_solution_" + test.id, data.error);
           return;
         }
         Model.refreshAllSubjects(callback);
  });
}

/**
 * Adds message.
 * @public
 * 
 * @param {object{read, from, title, content, time}}
 * @param {string}
 * @param {string}
 */
Model.addMessage = function(message, runningId, callback){
  if(typeof message !== "object" ||
     typeof message.content !== "string" ||
     typeof runningId !== "string") {
    return error(arguments);
  }
  $.post("/add_message",
  {
    "runningTestId": runningId,
    "content": message.content,
  },
  function(data, status){
    if(typeof data.error === "string"){
        View.errorShow("running_options_comment_button_" + message.id, data.error);
        return;
    }
    Model.refreshAllSubjects(callback);
    log("Model: Message added");
  });
}

/**
 * Removes message.
 * @public
 * 
 * @param {string}
 */
Model.removeMessage = function(messageId){
  if(typeof messageId !== "string"){
    return error(arguments);
  }
  $.post("/remove_message",
  {
    "id": messageId
  },
  function(data, status){
    var subjectId = "-1";
    var testId = "-1";
    var runningId = "-1";
    for(var sId in Model.subjects_){
      for(var tId in Model.subjects_[sId].tests){
          for(var rId in Model.subjects_[sId].tests[tId].running){
            for(var mId in subject[sId].tests[tId].running[rId].messages){
              if(mId !== messageId) continue;
              subjectId = sId;
              testId = tId;
              runningId = rId;
              break;
            }
          if(runningId !== "-1") break;
          }
        if(testId !== "-1") break;
      }
      if(subjectId !== "-1") break;
    }
    if(subjectId === "-1" || testId === "-1"){
    }
    Model.refreshAllSubjects(callback);
  });
}

/** @public */
Model.getUser = function(){
  return Model.user_.name;
}
